﻿using System;
using System.Collections.Generic;
using System.Text;
using DivaScript;
using System.Linq;
using EditMode;
using System.IO;

namespace ScriptUtilities
{
    public static class EditScript
    {
        private const int PV_FACTOR = 1000;

        private const int PV_START_FACTOR = 100000;

        /// <summary>
        /// The ratio between one edit mode frame unit and diva time.
        /// </summary>
        private const float PV_TIME_FACTOR = 1666.666f;

        /// <summary>
        /// If a duration is part of a command paramter then that duration is measured in milliseconds.
        /// </summary>
        private const float PV_DURATION_FACTOR = 16.666f;

        //public static Int32 ConvertRotation(float rotation) => (int)((rotation * PV_FACTOR) - 5000);
        public static int ConvertRotation(float rotation) => (int)(rotation * PV_FACTOR);

        public static int ConvertToDscUnits(float editUnits) => (int)(Math.Round(editUnits * PV_FACTOR));

        public static int ConvertDuration(float editDurationUnits) => (int)(Math.Round(editDurationUnits * PV_DURATION_FACTOR));

        public static (List<PvCommand> commands, PvDatabaseInfo pvDatabase) GetEditCommands(Edit edit, int pvId = 0)
        {
            PvDatabaseInfo database = new PvDatabaseInfo(edit, pvId);

            List<PvCommand> pvCommands = new List<PvCommand>();
            // MUSIC_PLAY
            pvCommands.Add(GetMusicPlayCommand(edit));
            // PV_END_FADEOUT, PV_END, END
            pvCommands.AddRange(GetPVEndCommands(edit));
            // SET_CHARA, EDIT_DISP
            pvCommands.AddRange(GetModelDisplayCommands(edit));
            // SET_CHARA, PARTS_DISP
            pvCommands.AddRange(GetPartsDisplayCommands(edit));
            // SET_CHARA, EDIT_SHADOW
            pvCommands.AddRange(GetShadowCommands(edit));
            // SET_CHARA, EDIT_MOVE_XYZ
            pvCommands.AddRange(GetPlayerMoveCommands(edit));
            // SET_CHARA, EDIT_MOT_SMOOTH_LEN, EDIT_MOTION / EDIT_MOTION_LOOP
            pvCommands.AddRange(GetMotionCommands(edit, database));
            // SET_CHARA, EDIT_ITEM, EDIT_INSTRUMENT_ITEM
            pvCommands.AddRange(GetItemCommands(edit, database));
            // SET_CHARA, EDIT_EXPRESSION
            pvCommands.AddRange(GetExpressionCommands(edit));
            // SET_CHARA, EDIT_BLUSH
            pvCommands.AddRange(GetBlushCommands(edit));
            // SET_CHARA, EDIT_EYE_ANIM
            pvCommands.AddRange(GetEyeAnimationCommands(edit));
            // SET_CHARA, EDIT_EYELID_ANIM
            pvCommands.AddRange(GetEyelidAnimationCommands(edit));
            // SET_CHARA, HAND_ANIM
            pvCommands.AddRange(GetHandAnimationCommands(edit));
            // SET_CHARA, EDIT_MOUTH_ANIM
            pvCommands.AddRange(GetMouthAnimationCommands(edit));
            // EDIT_CHANGE_FIELD, EDIT_STAGE_PARAM
            pvCommands.AddRange(GetStageCommands(edit, database));
            // EDIT_CAMERA / EDIT_CAMERA_BOX
            pvCommands.AddRange(GetCameraCommands(edit));
            // EDIT_EFFECT
            pvCommands.AddRange(GetEffectCommands(edit, database));
            // SCENE_FADE
            pvCommands.AddRange(GetFadeCommands(edit));
            // EDIT_LYRIC
            pvCommands.AddRange(GetLyricCommands(edit, database));
            // EDIT_TARGET
            pvCommands.AddRange(GetTargetCommands(edit));
            // EDIT_MODE_SELECT
            //pvCommands.AddRange(GetModeCommands(edit));

            pvCommands = PvScript.AddTimeCommands(pvCommands);
            pvCommands = PvScript.OrderListByTime(pvCommands);

            return (pvCommands, database);
        }

        public static (List<PvCommand> commands, PvDatabaseInfo pvDatabase) GetFtEditCommands(Edit edit, int pvId = 0)
        {
            PvDatabaseInfo database = new PvDatabaseInfo(edit, pvId);

            List<PvCommand> pvCommands = new List<PvCommand>();
            // MUSIC_PLAY
            pvCommands.Add(GetMusicPlayCommand(edit));
            // PV_END_FADEOUT, PV_END, END
            pvCommands.AddRange(GetPVEndCommands(edit));
            // SET_CHARA, EDIT_DISP
            pvCommands.AddRange(GetModelDisplayCommands(edit));
            // SET_CHARA, PARTS_DISP
            pvCommands.AddRange(GetPartsDisplayCommands(edit));
            // SET_CHARA, EDIT_SHADOW
            pvCommands.AddRange(GetShadowCommands(edit));
            // SET_CHARA, EDIT_MOVE_XYZ
            pvCommands.AddRange(GetPlayerMoveCommands(edit));
            // SET_CHARA, EDIT_MOTION_F
            pvCommands.AddRange(GetFtMotionCommands(edit, database));
            // SET_CHARA, EDIT_ITEM, EDIT_INSTRUMENT_ITEM
            pvCommands.AddRange(GetItemCommands(edit, database));
            // SET_CHARA, EDIT_EXPRESSION
            pvCommands.AddRange(GetExpressionCommands(edit));
            // SET_CHARA, EDIT_BLUSH
            pvCommands.AddRange(GetBlushCommands(edit));
            // SET_CHARA, EDIT_EYE_ANIM
            pvCommands.AddRange(GetEyeAnimationCommands(edit));
            // SET_CHARA, EDIT_EYELID_ANIM
            pvCommands.AddRange(GetEyelidAnimationCommands(edit));
            // SET_CHARA, HAND_ANIM
            pvCommands.AddRange(GetHandAnimationCommands(edit));
            // SET_CHARA, EDIT_MOUTH_ANIM
            pvCommands.AddRange(GetFtMouthAnimationCommands(edit));
            // EDIT_CHANGE_FIELD, EDIT_STAGE_PARAM
            pvCommands.AddRange(GetStageCommands(edit, database));
            // EDIT_CAMERA / EDIT_CAMERA_BOX
            pvCommands.AddRange(GetCameraCommands(edit));
            // EDIT_EFFECT
            pvCommands.AddRange(GetEffectCommands(edit, database));
            // SCENE_FADE
            pvCommands.AddRange(GetFadeCommands(edit));
            // EDIT_LYRIC
            pvCommands.AddRange(GetLyricCommands(edit, database));
            // EDIT_TARGET
            pvCommands.AddRange(GetFtTargetCommands(edit));
            // EDIT_MODE_SELECT
            //pvCommands.AddRange(GetModeCommands(edit));

            pvCommands = PvScript.AddTimeCommands(pvCommands);
            pvCommands = PvScript.OrderListByTime(pvCommands);

            return (pvCommands, database);
        }

        // music play
        internal static PvCommand GetMusicPlayCommand(Edit edit)
        {
            // MUSIC_PLAY(void);
            return new PvCommand((int)(edit.SongStartTime * PV_START_FACTOR) * -1, "MUSIC_PLAY", new List<int>());
        }

        // end
        internal static List<PvCommand> GetPVEndCommands(Edit edit)
        {
            // PV_END(void);
            var commands = new List<PvCommand>();
            int endTime = (int)(edit.PvEnd * PV_TIME_FACTOR);
            //commands.Add(new PvCommand(endTime, "PV_END_FADEOUT", new List<int> { 1000, 0 }));
            commands.Add(new PvCommand(endTime + 0, "PV_END", new List<int>()));
            commands.Add(new PvCommand(endTime + 1, "END", new List<int>()));
            return commands;
        }

        // model display
        internal static List<PvCommand> GetModelDisplayCommands(Edit edit)
        {
            // EDIT_DISP(display_state);
            var commands = new List<PvCommand>();
            AddShowModules(edit.ShowModulePlayer1s, 0);
            AddShowModules(edit.ShowModulePlayer2s, 1);
            AddShowModules(edit.ShowModulePlayer3s, 2);
            return commands;

            void AddShowModules(List<PlayerShowModule> playerShowModules, int player)
            {
                foreach (var instruction in playerShowModules)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_DISP", new List<int> { instruction.ShowPlayer }));
                }
            }
        }

        // parts display
        internal static List<PvCommand> GetPartsDisplayCommands(Edit edit)
        {
            // 19 頭, 18 顔, 20 胸, 21 背中
            // PARTS_DISP(player_id, part, display_state);
            var commands = new List<PvCommand>();
            AddShowMisc(edit.ShowMiscPlayer1s, 0);
            AddShowMisc(edit.ShowMiscPlayer2s, 1);
            AddShowMisc(edit.ShowMiscPlayer3s, 2);
            return commands;

            void AddShowMisc(List<PlayerShowMisc> playerShowMiscs, int player)
            {
                foreach (var instruction in playerShowMiscs)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "PARTS_DISP", new List<int>() { player, 19, instruction.Head }));
                    commands.Add(new PvCommand(time, "PARTS_DISP", new List<int>() { player, 18, instruction.Face }));
                    commands.Add(new PvCommand(time, "PARTS_DISP", new List<int>() { player, 20, instruction.Chest }));
                    commands.Add(new PvCommand(time, "PARTS_DISP", new List<int>() { player, 21, instruction.Back }));
                }
            }
        }

        // shadow
        internal static List<PvCommand> GetShadowCommands(Edit edit)
        {
            // EDIT_SHADOW(display_state);
            var commands = new List<PvCommand>();
            AddShadows(edit.ShowShadowPlayer1s, 0);
            AddShadows(edit.ShowShadowPlayer2s, 1);
            AddShadows(edit.ShowShadowPlayer3s, 2);
            return commands;

            void AddShadows(List<PlayerShowShadow> playerShowShadows, int player)
            {
                foreach (var instruction in playerShowShadows)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_SHADOW", new List<int>() { instruction.ShowShadow }));
                }
            }
        }

        // move
        internal static List<PvCommand> GetPlayerMoveCommands(Edit edit)
        {
            // EDIT_MOVE_XYZ(duration_ms, start_x, start_y, start_z, end_x, end_y, end_z, startRot, endRot);
            var commands = new List<PvCommand>();
            AddPositions(edit.PlayerPositionPlayer1s, 0);
            AddPositions(edit.PlayerPositionPlayer2s, 1);
            AddPositions(edit.PlayerPositionPlayer3s, 2);
            return commands;

            void AddPositions(List<PlayerPosition> playerPositions, int player)
            {
                foreach (var instruction in playerPositions)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_MOVE_XYZ", new List<int>()
                    {
                        ConvertDuration(instruction.Duration),
                        ConvertToDscUnits(instruction.Start.X),
                        ConvertToDscUnits(instruction.Start.Y),
                        ConvertToDscUnits(instruction.Start.Z),
                        ConvertToDscUnits(instruction.End.X),
                        ConvertToDscUnits(instruction.End.Y),
                        ConvertToDscUnits(instruction.End.Z),
                        ConvertToDscUnits(instruction.StartRoation),
                        ConvertToDscUnits(instruction.EndRoation),
                    }));
                }
            }
        }

        // motions
        internal static List<PvCommand> GetMotionCommands(Edit edit, PvDatabaseInfo database)
        {
            // EDIT_MOT_SMOOTH_LEN(player_id, interpolation);
            // EDIT_MOTION(motion_id, playback_speed, is_interpolated, playback_start_time);
            // EDIT_MOTION_LOOP(motion_id, playback_speed, is_interpolated, playback_start_time);
            var commands = new List<PvCommand>();
            AddMotions(edit.PlayerMotionPlayer1s, database.Motions1P, 0);
            AddMotions(edit.PlayerMotionPlayer2s, database.Motions2P, 1);
            AddMotions(edit.PlayerMotionPlayer3s, database.Motions3P, 2);
            return commands;

            void AddMotions(List<PlayerMotion> playerMotions, List<string> motions, int player)
            {
                foreach (var instruction in playerMotions)
                {
                    int motionId = motions.FindIndex(s => s == $"{instruction.MotionID1}");
                    int isInterpolated = Convert.ToInt32(instruction.Interpolation != 0);
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;

                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_MOT_SMOOTH_LEN", new List<int>() { player, instruction.Interpolation }));
                    commands.Add(new PvCommand(time, "EDIT_MOTION", new List<int>()
                    {
                        motionId,
                        ConvertToDscUnits(instruction.PlaybackSpeed),
                        isInterpolated,
                        (int)(instruction.PlaybackStartTime)
                    }));
                }
            }
        }

        // motions
        internal static List<PvCommand> GetFtMotionCommands(Edit edit, PvDatabaseInfo database)
        {
            // SET_CHARA(player_id);
            // EDIT_MOT_SMOOTH_LEN(player_id, interpolation);
            // EDIT_MOTION_F(motion_id, playback_speed, is_interpolated, playback_start_time, more_interpolation_stuff, more_interpolation_stuff);
            var commands = new List<PvCommand>();
            AddMotions(edit.PlayerMotionPlayer1s, database.Motions1P, 0);
            AddMotions(edit.PlayerMotionPlayer2s, database.Motions2P, 1);
            AddMotions(edit.PlayerMotionPlayer3s, database.Motions3P, 2);
            return commands;

            void AddMotions(List<PlayerMotion> playerMotions, List<string> motions, int player)
            {
                foreach (var instruction in playerMotions)
                {
                    int motionId = motions.FindIndex(s => s == $"{instruction.MotionID1}");
                    int isInterpolated = instruction.Interpolation != 0 ? 2 : 0;
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;

                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "MOT_SMOOTH", new List<int>() { player, instruction.Interpolation }));
                    commands.Add(new PvCommand(time, "EDIT_MOTION_F", new List<int>()
                    {
                        motionId,
                        ConvertToDscUnits(instruction.PlaybackSpeed),
                        isInterpolated,
                        (int)(instruction.PlaybackStartTime),
                        isInterpolated,
                        isInterpolated
                    }));
                }
            }
        }

        // item
        // TODO: think about hand animation
        internal static List<PvCommand> GetItemCommands(Edit edit, PvDatabaseInfo database)
        {
            // SET_CHARA(player_id);
            // EDIT_ITEM(item_id);
            // EDIT_INSTRUMENT_ITEM(item_id, playback_speed);
            var commands = new List<PvCommand>();
            AddItems(edit.PlayerItemPlayer1s, edit.PlayerMotionPlayer1s, 0);
            AddItems(edit.PlayerItemPlayer2s, edit.PlayerMotionPlayer2s, 1);
            AddItems(edit.PlayerItemPlayer3s, edit.PlayerMotionPlayer3s, 2);
            return commands;

            void AddItems(List<PlayerItem> playerItem, List<PlayerMotion> motions, int player)
            {
                foreach (var instruction in playerItem)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;

                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));

                    if (Enum.IsDefined(typeof(HandItem), (HandItem)instruction.ItemID))
                    {
                        int itemId = database.HandItems.FindIndex(i => i == (HandItem)instruction.ItemID);

                        if (itemId != -1)
                            itemId++;

                        commands.Add(new PvCommand(time, "EDIT_ITEM", new List<int>() { itemId }));
                    }
                    else if (Enum.IsDefined(typeof(InstrumentItem), (InstrumentItem)instruction.ItemID))
                    {
                        int itemId = database.PvItems.FindIndex(i => i == (InstrumentItem)instruction.ItemID);
                        var motionInstruction = motions.FirstOrDefault(m => m.Time == instruction.Time);
                        int speed = motionInstruction != null ? ConvertToDscUnits(motionInstruction.PlaybackSpeed) : 0;

                        commands.Add(new PvCommand(time, "EDIT_INSTRUMENT_ITEM", new List<int>() { itemId, speed }));
                    }
                    else // Item.なし
                    {
                        commands.Add(new PvCommand(time, "EDIT_ITEM", new List<int>() { -1 }));
                    }
                }
            }
        }

        // expression
        internal static List<PvCommand> GetExpressionCommands(Edit edit)
        {
            // SET_CHARA(player_id);
            // EDIT_EXPRESSION(expression_id, interpolation);
            var commands = new List<PvCommand>();
            AddExpressions(edit.PlayerExpressionPlayer1s, 0);
            AddExpressions(edit.PlayerExpressionPlayer2s, 1);
            AddExpressions(edit.PlayerExpressionPlayer3s, 2);
            return commands;

            void AddExpressions(List<PlayerExpression> playerExpressions, int player)
            {
                foreach (var instruction in playerExpressions)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_EXPRESSION", new List<int>() { (int)instruction.ExpressionID, (int)(instruction.Interpolation) }));
                }
            }
        }

        // blush
        internal static List<PvCommand> GetBlushCommands(Edit edit)
        {
            // SET_CHARA(player_id);
            // EDIT_BLUSH(blush_id);
            var commands = new List<PvCommand>();
            AddBlushes(edit.PlayerFaceEffectPlayer1s, 0);
            AddBlushes(edit.PlayerFaceEffectPlayer2s, 1);
            AddBlushes(edit.PlayerFaceEffectPlayer3s, 2);
            return commands;

            void AddBlushes(List<PlayerFaceEffect> playerFaceEffects, int player)
            {
                foreach (var instruction in playerFaceEffects)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_BLUSH", new List<int>() { (int)instruction.FaceID }));
                }
            }
        }

        // eye anim
        internal static List<PvCommand> GetEyeAnimationCommands(Edit edit)
        {
            // SET_CHARA(player_id);
            // EDIT_EYE_ANIM(eye_id, 0, interpolation);
            var commands = new List<PvCommand>();
            AddEyeAnimations(edit.PlayerLineOfSightPlayer1s, 0);
            AddEyeAnimations(edit.PlayerLineOfSightPlayer2s, 1);
            AddEyeAnimations(edit.PlayerLineOfSightPlayer3s, 2);
            return commands;

            void AddEyeAnimations(List<PlayerLineOfSight> playerLineOfSights, int player)
            {
                foreach (var instruction in playerLineOfSights)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_EYE_ANIM", new List<int>() { (int)instruction.SightID, 0, (int)instruction.Interpolation }));
                }
            }
        }

        // eyelid anim
        internal static List<PvCommand> GetEyelidAnimationCommands(Edit edit)
        {
            // SET_CHARA(player_id)
            // EDIT_EYELID_ANIM(eyelid_settings, interpolation, closure_level)
            var commands = new List<PvCommand>();
            AddEyelidAnimations(edit.PlayerBlinkPlayer1s, 0);
            AddEyelidAnimations(edit.PlayerBlinkPlayer2s, 1);
            AddEyelidAnimations(edit.PlayerBlinkPlayer3s, 2);
            return commands;

            void AddEyelidAnimations(List<PlayerBlink> playerBlinks, int player)
            {
                foreach (var instruction in playerBlinks)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_EYELID_ANIM", new List<int>() { (int)instruction.BlinkID, (int)instruction.Interpolation, instruction.BlinkLevel }));
                }
            }
        }

        // hand anim
        internal static List<PvCommand> GetHandAnimationCommands(Edit edit)
        {
            // 0 左, 1 右
            // HAND_ANIM(player_id, hand_side, hand_id, interpolation, hand_item);
            var commands = new List<PvCommand>();
            AddPlayerHands(edit.PlayerLeftHandPlayer1s, 0, 0);
            AddPlayerHands(edit.PlayerLeftHandPlayer2s, 1, 0);
            AddPlayerHands(edit.PlayerLeftHandPlayer3s, 2, 0);
            AddPlayerHands(edit.PlayerRightHandPlayer1s, 0, 1);
            AddPlayerHands(edit.PlayerRightHandPlayer2s, 1, 1);
            AddPlayerHands(edit.PlayerRightHandPlayer3s, 2, 1);
            return commands;

            void AddPlayerHands(List<PlayerHand> playerHands, int player, int hand)
            {
                foreach (var instruction in playerHands)
                {
                    int handItem = -1;
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "HAND_ANIM", new List<int>() { player, hand, (int)instruction.HandID, (int)instruction.Interpolation, handItem }));
                }
            }
        }

        // mouth anim
        internal static List<PvCommand> GetMouthAnimationCommands(Edit edit)
        {
            // SET_CHARA(player_id);
            // EDIT_MOUTH_ANIM(mouth_shape, interpolation);
            var commands = new List<PvCommand>();
            AddMouthAnimations(edit.PlayerLipsyncPlayer1s, 0);
            AddMouthAnimations(edit.PlayerLipsyncPlayer2s, 1);
            AddMouthAnimations(edit.PlayerLipsyncPlayer3s, 2);
            return commands;

            void AddMouthAnimations(List<PlayerLipsync> playerLipsyncs, int player)
            {
                foreach (var instruction in playerLipsyncs)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));
                    commands.Add(new PvCommand(time, "EDIT_MOUTH_ANIM", new List<int>() { (int)instruction.MouthID, (int)instruction.Interpolation }));
                }
            }
        }

        // mouth anim
        internal static List<PvCommand> GetFtMouthAnimationCommands(Edit edit)
        {
            // SET_CHARA(player_id);
            // EDIT_MOUTH_ANIM(mouth_shape, interpolation);
            var commands = new List<PvCommand>();
            AddMouthAnimations(edit.PlayerLipsyncPlayer1s, 0);
            AddMouthAnimations(edit.PlayerLipsyncPlayer2s, 1);
            AddMouthAnimations(edit.PlayerLipsyncPlayer3s, 2);
            return commands;

            void AddMouthAnimations(List<PlayerLipsync> playerLipsyncs, int player)
            {
                foreach (var instruction in playerLipsyncs)
                {
                    int time = (int)(instruction.Time * PV_TIME_FACTOR) + player;
                    commands.Add(new PvCommand(time, "SET_CHARA", new List<int> { player }));

                    if (Enum.TryParse(instruction.MouthID.ToString(), out FtMouth ftMouth))
                        commands.Add(new PvCommand(time, "EDIT_MOUTH_ANIM", new List<int>() { (int)ftMouth, (int)instruction.Interpolation }));
                    else
                        commands.Add(new PvCommand(time, "EDIT_MOUTH_ANIM", new List<int>() { (int)FtMouth.通常, (int)instruction.Interpolation }));
                }
            }
        }

        // stage
        internal static List<PvCommand> GetStageCommands(Edit edit, PvDatabaseInfo database)
        {
            // CHANGE_FIELD(stage_id);
            var commands = new List<PvCommand>();
            foreach (var instruction in edit.EditStages)
            {
                // TODO: add SATURATION etc.
                int stageID = database.Stages.FindIndex(s => (instruction.StageID1 == Stage.SPR_EDIT_BLACK) ? s == instruction.StageID3 : s == instruction.StageID1) + 1;
                int time = (int)(instruction.Time * PV_TIME_FACTOR) + 0;
                commands.Add(new PvCommand(time, "CHANGE_FIELD", new List<int>() { stageID }));
            }
            return commands;
        }

        // camera
        internal static List<PvCommand> GetCameraCommands(Edit edit)
        {
            // EDIT_CAMERA(duration,
            //     start_x, start_y, start_z,
            //     start_focus_x, start_focus_y, start_focus_z,
            //     start_unk_x, start_unk_y, start_unk_z,
            //     end_x, end_y, end_z,
            //     end_focus_x, end_focus_y, end_focus_z,
            //     end_unk_x, end_unk_y, end_unk_z,
            //     acceleration_x, acceleration_y,
            //     is_player_focus, player_focus, body_part_focus);
            var commands = new List<PvCommand>();
            foreach (var camera in edit.EditCameras)
            {
                int time = (int)(camera.Time * PV_TIME_FACTOR) + 0;
                foreach (var instruction in camera.CameraKeyPoints)
                {
                    if (instruction.CameraDuration <= 0)
                        continue;

                    commands.Add(new PvCommand(time, "EDIT_CAMERA", new List<int>()
                    {
                        ConvertDuration(instruction.CameraDuration),

                        ConvertToDscUnits(instruction.CameraStart.X),
                        ConvertToDscUnits(instruction.CameraStart.Y),
                        ConvertToDscUnits(instruction.CameraStart.Z),

                        ConvertToDscUnits(instruction.FocusPointStart.X),
                        ConvertToDscUnits(instruction.FocusPointStart.Y),
                        ConvertToDscUnits(instruction.FocusPointStart.Z),

                        ConvertToDscUnits(instruction.UnknowntStart.X),
                        ConvertToDscUnits(instruction.UnknowntStart.Y),
                        ConvertToDscUnits(instruction.UnknowntStart.Z),

                        ConvertToDscUnits(instruction.CameraEnd.X),
                        ConvertToDscUnits(instruction.CameraEnd.Y),
                        ConvertToDscUnits(instruction.CameraEnd.Z),

                        ConvertToDscUnits(instruction.FocusPointEnd.X),
                        ConvertToDscUnits(instruction.FocusPointEnd.Y),
                        ConvertToDscUnits(instruction.FocusPointEnd.Z),

                        ConvertToDscUnits(instruction.UnknowntEnd.X),
                        ConvertToDscUnits(instruction.UnknowntEnd.Y),
                        ConvertToDscUnits(instruction.UnknowntEnd.Z),

                        ConvertToDscUnits(instruction.CameraAccelerationStart),
                        ConvertToDscUnits(instruction.CameraAccelerationEnd),
                        //(int)(instruction.CameraAccelerationEnd * 0),

                        GetIsPlayerFocus(instruction.PlayerFocus),
                        GetPlayer(instruction.PlayerFocus),
                        GetPart(instruction.PlayerFocus),
                    }));
                    time += (int)(instruction.CameraDuration * PV_TIME_FACTOR);
                }
            }
            return commands;

            int GetIsPlayerFocus(PlayerFocus pf)
            {
                if (pf == PlayerFocus.なし || !Enum.IsDefined(typeof(PlayerFocus), pf))
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            }
            int GetPlayer(PlayerFocus pf)
            {
                switch (pf)
                {
                    case PlayerFocus.なし:
                        return 0;
                    case PlayerFocus.P1_顔:
                    case PlayerFocus.P1_胸:
                    case PlayerFocus.P1_腰:
                        return 0;
                    case PlayerFocus.P2_顔_0:
                    case PlayerFocus.P2_顔_1:
                    case PlayerFocus.P2_胸:
                    case PlayerFocus.P2_腰:
                        return 1;
                    case PlayerFocus.P3_顔:
                    case PlayerFocus.P3_胸:
                    case PlayerFocus.P3_腰:
                        return 2;
                    default:
                        return -1;
                }
            }
            int GetPart(PlayerFocus pf)
            {
                switch (pf)
                {
                    case PlayerFocus.なし:
                        return 0;
                    case PlayerFocus.P1_顔:
                    case PlayerFocus.P2_顔_0:
                    case PlayerFocus.P2_顔_1:
                    case PlayerFocus.P3_顔:
                        return 0;
                    case PlayerFocus.P1_胸:
                    case PlayerFocus.P2_胸:
                    case PlayerFocus.P3_胸:
                        return 1;
                    case PlayerFocus.P1_腰:
                    case PlayerFocus.P2_腰:
                    case PlayerFocus.P3_腰:
                        return 2;
                    default:
                        return 0;
                }
            }
        }

        // effect
        internal static List<PvCommand> GetEffectCommands(Edit edit, PvDatabaseInfo database)
        {
            // EDIT_EFFECT(effect_id, playback_speed);
            var commands = new List<PvCommand>();
            foreach (var instruction in edit.EditEffects)
            {
                int effectID = (instruction.EffectID == Effect.なし) ? -1 : database.Effects.FindIndex(s => s == instruction.EffectID);
                int time = (int)(instruction.Time * PV_TIME_FACTOR) + 0;
                commands.Add(new PvCommand(time, "EDIT_EFFECT", new List<int>() { effectID, ConvertToDscUnits(instruction.PlaybackSpeed) }));
            }
            return commands;
        }

        // fade
        internal static List<PvCommand> GetFadeCommands(Edit edit)
        {
            // SCENE_FADE(playback_speed, playback_start_time, start_intensity, r, g, b);
            var commands = new List<PvCommand>();
            foreach (var instruction in edit.EditFades)
            {
                int time = (int)(instruction.Time * PV_TIME_FACTOR) + 0;
                FadeColor color = new FadeColor(instruction.FadeType);

                bool isFadeIn = instruction.GetIsFadeIn();
                commands.Add(new PvCommand(time, "SCENE_FADE", new List<int>()
                {
                    (ConvertToDscUnits(instruction.PlaybackSpeed)),
                    ((int)((isFadeIn ? 1000 : 0) - (instruction.StartTime * (isFadeIn ? 10 : -10) ))),
                    //((int)(instruction.EffectProgress * 10)),
                    //(instruction.PlaybackSpeed == 0) ? (int)(instruction.EffectProgress * 10) : color.A,
                    ((instruction.FadeType == Fade.なし) ? 0 : ((int)(instruction.EffectProgress * 10))),
                    color.R,
                    color.G,
                    color.B,
                }));
            }
            return commands;
            //SCENE_FADE(002000, 200, 0000, 1000, 1000, 1000)
            //SCENE_FADE(003000, 160, 1000, 0000, 0000, 0000)
            //SCENE_FADE(004000, 327, 0000, 0000, 0000, 0000)
            //SCENE_FADE(005000, 312, 1000, 1000, 0000, 0000)
            //SCENE_FADE(006000, 340, 1000, 1000, 0000, 0000)
            //SCENE_FADE(007000, 362, 0000, 1000, 0000, 0000)
            //SCENE_FADE(008000, 352, 1000, 0000, 1000, 0000)
            //SCENE_FADE(009000, 368, 0000, 0000, 1000, 0000)
            //SCENE_FADE(010000, 359, 1000, 0000, 0000, 1000)
            //SCENE_FADE(011000, 373, 0000, 0000, 0000, 1000)
            //SCENE_FADE(012000, 366, 1000, 1000, 1000, 0000)
            //SCENE_FADE(013000, 377, 0000, 1000, 1000, 0000)
            //SCENE_FADE(014000, 371, 1000, 1000, 0753, 0796)
            //SCENE_FADE(015000, 380, 0000, 1000, 0753, 0796)
            //SCENE_FADE(016000, 375, 1000, 0000, 1000, 1000)
            //SCENE_FADE(018000, 383, 0000, 0000, 1000, 1000)
            //SCENE_FADE(019000, 379, 1000, 1000, 0645, 0000)
            //SCENE_FADE(020000, 385, 0000, 1000, 0645, 0000)
            //SCENE_FADE(021000, 382, 1000, 0500, 0000, 0500)
            //SCENE_FADE(022000, 388, 0000, 0500, 0000, 0500)
            //SCENE_FADE(169000, 000, 0000, 0500, 0000, 0500)
        }

        // lyrics
        internal static List<PvCommand> GetLyricCommands(Edit edit, PvDatabaseInfo database)
        {
            // LYRIC(lyric_id, bgra_color);
            var commands = new List<PvCommand>();
            for (int i = 0; i < edit.EditLyrics.Count; i++)
            {
                int lyricID = database.Lyrics.FindIndex(s => s == edit.EditLyrics[i].Lyrics);
                EditLyric instruction = edit.EditLyrics[i];
                int time = (int)(instruction.Time * PV_TIME_FACTOR) + 0;
                int endTime = time + (int)(instruction.Duration * PV_TIME_FACTOR) - 1;
                commands.Add(new PvCommand(time, "LYRIC", new List<int>() { lyricID, (int)GetBGRA(instruction) }));
                commands.Add(new PvCommand(endTime, "LYRIC", new List<int>() { -1, (int)GetBGRA(instruction) }));
            }
            return commands;

            uint GetBGRA(EditLyric lyric) => (uint)((lyric.B << 0) | (lyric.G << 8) | (lyric.R << 16) | ((lyric.Alpha = 0xFF) << 24));
        }

        // targets
        internal static List<PvCommand> GetTargetCommands(Edit edit)
        {
            EditTime.NormalizeTimelineIndexes(edit);

            // TARGET(target_type, hold_Duration, is_hold_end, x, y, entry_angle, button_frequency, button_distance, button_amplitude, button_fly_duration, time_sig, end_int);
            var targetCommands = new List<PvCommand>();
            foreach (var t in edit.EditTargets)
            {
                var offset = 1;
                //offset = 4 - EditTime.GetSigAt(t.BarIndex - 1, edit.TimeSigChanges);
                //int targetTime = EditTime.DivaTimeFromTimelineIndex((ushort)(t.BarIndex - offset), t.BeatIndex, edit.BPMChanges, edit.TimeSigChanges);
                int targetTime = EditTime.DivaTimeFromNormalizedTimelineIndex((ushort)(t.BarIndex - offset), t.BeatIndex, edit.BPMChanges);
                //int buttonTime = EditTime.DivaTimeFromTimelineIndex(t.BarIndex, t.BeatIndex, edit.BPMChanges, edit.TimeSigChanges);
                int buttonTime = EditTime.DivaTimeFromNormalizedTimelineIndex(t.BarIndex, t.BeatIndex, edit.BPMChanges);
                int flyDuration = buttonTime - targetTime;

                const float POSITION_FACTOR = 362.5f;

                bool isHold = t.HoldEndBar != 0 || t.HoldEndBeat != 0;

                //int longTargetTime = isHold ? EditTime.DivaTimeFromTimelineIndex((ushort)(t.HoldEndBar - 1), t.HoldEndBeat, edit.BPMChanges, edit.TimeSigChanges) : 0;
                int longTargetTime = isHold ? EditTime.DivaTimeFromNormalizedTimelineIndex((ushort)(t.HoldEndBar - 1), t.HoldEndBeat, edit.BPMChanges) : 0;
                //int longButtonTime = isHold ? EditTime.DivaTimeFromTimelineIndex(t.HoldEndBar, t.HoldEndBeat, edit.BPMChanges, edit.TimeSigChanges) : 0;
                int longButtonTime = isHold ? EditTime.DivaTimeFromNormalizedTimelineIndex(t.HoldEndBar, t.HoldEndBeat, edit.BPMChanges) : 0;
                int longFlyDuration = isHold ? longButtonTime - longTargetTime : 0;
                int holdDuration = isHold ? longTargetTime - targetTime : 0;

                int x = (int)(t.XPosition * POSITION_FACTOR);
                int y = (int)(t.YPosition * POSITION_FACTOR);
                int angle = ConvertRotation(t.EntryAngle);
                int targetType = (int)t.TargetType;
                List<int> param = new List<int>(11)
                {
                    // [00] target_type
                    targetType,
                    // [01] hold_Duration
                    holdDuration, 
                    // [02] is_hold_end
                    -1, 
                    // [03] x_position
                    x, 
                    // [04] y_position
                    y,
                    // [05] entry_angle
                    angle,
                    // [06] button_frequency
                    2,
                    // [07] button_distance
                    300000, 
                    // [08] button_amplitude
                    500, 
                    // [09] button_fly_duration
                    flyDuration / 100,
                    // [10] time_signature
                    EditTime.GetSigAt(t.BarIndex, edit.TimeSigChanges),
                    // [11] filler
                    -1,
                };

                targetCommands.Add(new PvCommand(targetTime, "TARGET", param));

                if (isHold)
                {
                    List<int> longParam = new List<int>(11)
                    {
                        // [00] target_type
                        targetType,
                        // [01] hold_Duration
                        -1, 
                        // [02] is_hold_end
                        1, 
                        // [03] x_position
                        x, 
                        // [04] y_position
                        y,
                        // [05] entry_angle
                        angle,
                        // [06] button_frequency
                        2,
                        // [07] button_distance
                        300000, 
                        // [08] button_amplitude
                        500, 
                        // [09] button_fly_duration
                        longFlyDuration / 100,
                        // [10] time_signature
                        EditTime.GetSigAt(t.HoldEndBar, edit.TimeSigChanges),
                        // [11] filler
                        -1,
                    };
                    targetCommands.Add(new PvCommand(longTargetTime, "TARGET", longParam));
                }
            }
            return targetCommands;
        }

        // targets
        internal static List<PvCommand> GetFtTargetCommands(Edit edit)
        {
            EditTime.NormalizeTimelineIndexes(edit);

            // TARGET(target_type, x, y, entry_angle, button_distance, button_amplitude, button_frequency);
            var targetCommands = new List<PvCommand>();
            foreach (var t in edit.EditTargets)
            {
                var offset = 1;
                //int targetTime = EditTime.DivaTimeFromTimelineIndex((ushort)(t.BarIndex - offset), t.BeatIndex, edit.BPMChanges, edit.TimeSigChanges);
                int targetTime = EditTime.DivaTimeFromNormalizedTimelineIndex((ushort)(t.BarIndex - offset), t.BeatIndex, edit.BPMChanges);
                //int buttonTime = EditTime.DivaTimeFromTimelineIndex(t.BarIndex, t.BeatIndex, edit.BPMChanges, edit.TimeSigChanges);
                int buttonTime = EditTime.DivaTimeFromNormalizedTimelineIndex(t.BarIndex, t.BeatIndex, edit.BPMChanges);
                int flyDuration = buttonTime - targetTime;

                const float POSITION_FACTOR = 362.5f;

                bool isHold = t.HoldEndBar != 0 || t.HoldEndBeat != 0;

                //int longTargetTime = isHold ? EditTime.DivaTimeFromNormalizedTimelineIndex((ushort)(t.HoldEndBar - 1), t.HoldEndBeat, edit.BPMChanges) : 0;
                //int longButtonTime = isHold ? EditTime.DivaTimeFromNormalizedTimelineIndex(t.HoldEndBar, t.HoldEndBeat, edit.BPMChanges) : 0;
                //int longFlyDuration = isHold ? longButtonTime - longTargetTime : 0;
                //int holdDuration = isHold ? longTargetTime - targetTime : 0;

                int x = (int)(t.XPosition * POSITION_FACTOR);
                int y = (int)(t.YPosition * POSITION_FACTOR);
                int angle = ConvertRotation(t.EntryAngle);
                int targetType = (int)t.TargetType;
                List<int> param = new List<int>(11)
                {
                    // [00] target_type
                    targetType,
                    // [01] x_position
                    x, 
                    // [02] y_position
                    y,
                    // [03] entry_angle
                    angle,
                    // [04] button_distance
                    1200 * 250, 
                    // [05] button_amplitude
                    500,
                    // [06] button_frequency
                    2,
                };

                targetCommands.Add(new PvCommand(targetTime, "TARGET_FLYING_TIME", new List<int> { flyDuration / 100 }));
                targetCommands.Add(new PvCommand(targetTime, "TARGET", param));
            }
            return targetCommands;
        }

        // mode select
        // TODO: internal static List<PvCommand> GetModeCommands(Edit edit)

        private struct FadeColor
        {
            public int A;
            public int R;
            public int G;
            public int B;

            public FadeColor(int a, int r, int g, int b)
            {
                A = a;
                R = r;
                G = g;
                B = b;
            }

            public FadeColor(Fade fade)
            {
                var color = GetColor(fade);
                this = new FadeColor(color.A, color.R, color.G, color.B);
            }

            public static FadeColor GetColor(Fade fade)
            {
                switch (fade)
                {
                    case Fade.フェードアウト_白:
                        return new FadeColor(1000, 1000, 1000, 1000);
                    case Fade.フェードイン_白:
                        return new FadeColor(0000, 1000, 1000, 1000);
                    case Fade.フェードアウト_黒:
                        return new FadeColor(1000, 0000, 0000, 0000);
                    case Fade.フェードイン_黒:
                        return new FadeColor(0000, 0000, 0000, 0000);
                    case Fade.フェードアウト_赤:
                        return new FadeColor(1000, 1000, 0000, 0000);
                    case Fade.フェードイン_赤:
                        return new FadeColor(0000, 1000, 0000, 0000);
                    case Fade.フェードアウト_緑:
                        return new FadeColor(1000, 0000, 1000, 0000);
                    case Fade.フェードイン_緑:
                        return new FadeColor(0000, 0000, 1000, 0000);
                    case Fade.フェードアウト_青:
                        return new FadeColor(1000, 0000, 0000, 1000);
                    case Fade.フェードイン_青:
                        return new FadeColor(0000, 0000, 0000, 1000);
                    case Fade.フェードアウト_黄:
                        return new FadeColor(1000, 1000, 1000, 0000);
                    case Fade.フェードイン_黄:
                        return new FadeColor(0000, 1000, 1000, 0000);
                    case Fade.フェードアウト_ピンク:
                        return new FadeColor(1000, 1000, 0753, 0796);
                    case Fade.フェードイン_ピンク:
                        return new FadeColor(0000, 1000, 0753, 0796);
                    case Fade.フェードアウト_水色:
                        return new FadeColor(1000, 0000, 1000, 1000);
                    case Fade.フェードイン_水色:
                        return new FadeColor(0000, 0000, 1000, 1000);
                    case Fade.フェードアウト_橙:
                        return new FadeColor(1000, 1000, 0645, 0000);
                    case Fade.フェードイン_橙:
                        return new FadeColor(0000, 1000, 0645, 0000);
                    case Fade.フェードアウト_紫:
                        return new FadeColor(1000, 0500, 0000, 0500);
                    case Fade.フェードイン_紫:
                        return new FadeColor(0000, 0500, 0000, 0500);
                    case Fade.なし:
                    default:
                        return new FadeColor(0000, 0000, 0000, 0000);
                }
            }
        }

        public static void CheckForFtMotions()
        {
            StringBuilder sb = new StringBuilder();

            string path = @"C:\Dev\EditSaveData\SaveData";

            string[] creatorDirectories = Directory.GetDirectories(path);

            const string EDIT_CONTAINER = "SECURE.BIN";
            const string EX_PREFIX = "EXEDIT";
            foreach (var creatorDirectory in creatorDirectories)
            {
                foreach (var editDirectory in Directory.GetDirectories(creatorDirectory))
                {
                    string editPath = $"{editDirectory}\\{EDIT_CONTAINER}";
                    //Console.WriteLine($"editPath:{editPath} \n File.Exists(editPath){File.Exists(editPath)}");

                    bool isEdit = !editPath.Contains(EX_PREFIX);
                    if (isEdit)
                    {
                        try
                        {
                            Edit edit = Edit.Deserialize(editPath);
                            PvDatabaseInfo database = new PvDatabaseInfo(edit, 0);
                            var allMotions = database.Motions1P.Concat(database.Motions2P).Concat(database.Motions3P);

                            bool compatable = allMotions.Any(m => IsFtCompatable(m));
                            var compatableMotions = allMotions.Where(m => IsFtCompatable(m));

                            sb.Append($"[{edit.EditTitle} by {edit.EditCreator}] - compatible motions: ({compatableMotions.ToArray().Length}/{allMotions.ToArray().Length}) \n" +
                                $"{editPath} \n");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"{ex}");
                        }
                    }
                }
            }

            Console.WriteLine(sb);
            string outputPath = @"C:\Dev\EditSaveData\SaveData\compatibility_output_log.txt";
            File.WriteAllText(outputPath, sb.ToString());
        }

        internal static bool IsFtCompatable(string motion) => FT_EDIT_MOTIONS.Contains(motion);
        internal static bool IsFtCompatable(Motion motion) => IsFtCompatable(motion.ToString());

        internal static readonly List<string> FT_EDIT_MOTIONS = new List<string>()
        {
            "CMN_MRA00_13_01",
            "CMN_MCA00_50_10",
            "CMN_MCA00_50_12",
            "CMN_MCA00_50_13",
            "CMN_MCA00_50_17",
            "CMN_MCA00_50_18_B",
            "CMN_MCA00_50_19",
            "CMN_MCA00_50_20",
            "CMN_MCA00_50_21",
            "CMN_MCA00_50_22",
            "CMN_MCA00_50_23",
            "CMN_MCA00_50_24",
            "CMN_MCA00_50_25",
            "CMN_MCA00_50_26_A",
            "CMN_MCA00_50_26_B",
            "CMN_MCA00_50_27",
            "CMN_MCA00_50_28",
            "CMN_MCA00_50_29",
            "CMN_MCA00_50_30",
            "CMN_MCA00_50_31",
            "CMN_MCA00_50_32",
            "CMN_MCA00_50_33",
            "CMN_MCA00_50_34",
            "CMN_MCA00_50_35",
            "CMN_MCA00_50_36",
            "CMN_MCA00_50_37",
            "CMN_MCA00_50_38",
            "CMN_MCA00_50_39",
            "CMN_MCA00_50_40",
            "CMN_MCA00_50_41",
            "CMN_MCA00_50_42",
            "CMN_MCA00_50_43",
            "CMN_MCA00_50_46",
            "CMN_MCA00_50_47",
            "CMN_MCA00_50_48",
            "CMN_MCA00_50_49",
            "CMN_MCA00_50_50",
            "CMN_MCA00_50_51",
            "CMN_MCA00_50_53",
            "CMN_MCA00_50_54",
            "CMN_MCA00_50_55",
            "CMN_MCA00_50_56_A",
            "CMN_MCA00_50_56_B",
            "CMN_MCA00_50_57",
            "CMN_MCA00_50_58",
            "CMN_MCA00_50_60_A",
            "CMN_MCA00_50_60_B",
            "CMN_MCA00_50_60_C",
            "CMN_MCA00_70_11",
            "CMN_MCA00_70_12",
            "CMN_MCA00_70_13",
            "CMN_MCA00_70_14",
            "CMN_MCA00_70_15",
            "CMN_MCA00_70_16",
            "CMN_MCA00_70_17",
            "CMN_MCA00_70_19",
            "CMN_MCA00_70_20",
            "CMN_MCA00_70_21",
            "CMN_MCA00_70_22",
            "CMN_MCA00_70_23",
            "CMN_MCA00_70_24",
            "CMN_MCA00_70_25",
            "CMN_MCA00_70_26",
            "CMN_MCA00_70_28",
            "CMN_MCA00_70_29",
            "CMN_MCA00_70_30",
            "CMN_MCA00_70_31",
            "CMN_MCA00_70_32",
            "CMN_EDT_01_000A",
            "CMN_EDT_01_002A",
            "CMN_EDT_01_004A",
            "CMN_EDT_01_005A",
            "CMN_EDT_01_009A",
            "CMN_EDT_01_010A",
            "CMN_EDT_01_011A",
            "CMN_EDT_01_012A",
            "CMN_EDT_01_014A",
            "CMN_EDT_01_016A",
            "CMN_EDT_01_017A",
            "CMN_EDT_01_019A",
            "CMN_EDT_01_021A",
            "CMN_EDT_01_022A",
            "CMN_EDT_01_024A",
            "CMN_EDT_01_027A",
            "CMN_EDT_01_028A",
            "CMN_EDT_01_029A",
            "CMN_EDT_01_030A",
            "CMN_EDT_01_031A",
            "CMN_EDT_01_032A",
            "CMN_EDT_01_033A",
            "CMN_EDT_01_034A",
            "CMN_EDT_01_035A",
            "CMN_EDT_01_036A",
            "CMN_EDT_01_037A",
            "CMN_EDT_01_038A",
            "CMN_EDT_01_039A",
            "CMN_EDT_01_040A",
            "CMN_EDT_01_041A",
            "CMN_EDT_01_042A",
            "CMN_EDT_01_043A",
            "CMN_EDT_01_045A",
            "CMN_EDT_01_046A",
            "CMN_EDT_01_047A",
            "CMN_EDT_01_048A",
            "CMN_EDT_01_050A",
            "CMN_EDT_01_051A",
            "CMN_EDT_01_052A",
            "CMN_EDT_01_055A",
            "CMN_EDT_01_056A",
            "CMN_EDT_01_058A",
            "CMN_EDT_01_060A",
            "CMN_EDT_EX_PSP03_011A",
            "CMN_EDT_EX_PSP03_012A",
            "CMN_EDT_EX_PSP03_013A",
            "CMN_EDT_EX_PSP03_015A",
            "CMN_EDT_EX_PSP03_017A",
            "CMN_EDT_EX_PSP03_018A",
            "CMN_EDT_EX_PSP03_019A",
            "CMN_EDT_EX_PSP03_020A",
            "CMN_EDT_EX_PSP03_022A",
            "CMN_EDT_EX_PSP03_023A",
            "CMN_EDT_EX_PSP03_028A",
            "CMN_EDT_EX_PSP03_031A",
            "CMN_EDT_EX_PSP03_032A",
            "CMN_EDT_EX_PSP03_034A",
            "CMN_EDT_EX_PSP03_035A",
            "CMN_EDT_EX_PSP03_036A",
            "CMN_EDT_EX_PSP03_037A",
            "CMN_EDT_EX_PSP03_041A",
            "CMN_EDT_EX_PSP03_054A",
            "CMN_EDT_EX_PSP03_057A",
            "CMN_EDT_EX_PSP03_058A",
            "CMN_EDT_EX_PSP03_059A",
            "CMN_EDT_PS3_002A",
            "CMN_EDT_PS3_006A",
            "CMN_EDT_PS3_007A",
            "CMN_EDT_PS3_008A",
            "CMN_EDT_PS3_011A",
            "CMN_EDT_PS3_012A",
            "CMN_EDT_PS3_013A",
            "CMN_EDT_PS3_015A",
            "CMN_EDT_PS3_017A",
            "CMN_EDT_PS3_021A",
            "CMN_EDT_PS3_022A",
            "CMN_EDT_PS3_023A",
            "CMN_EDT_PSP01_50_10",
            "CMN_EDT_PSP01_50_12",
            "CMN_EDT_PSP01_50_18_B",
            "CMN_EDT_PSP01_50_21",
            "CMN_EDT_PSP01_50_22",
            "CMN_EDT_PSP01_50_23",
            "CMN_EDT_PSP01_50_28",
            "CMN_EDT_PSP01_50_29",
            "CMN_EDT_PSP01_50_42",
            "CMN_EDT_PSP01_50_50",
            "CMN_EDT_PSP01_50_53",
            "CMN_EDT_PSP01_50_54",
            "CMN_EDT_PSP01_50_56_B",
            "CMN_EDT_PSP01_50_60_A",
            "CMN_EDT_PSP01_70_11",
            "CMN_EDT_PSP01_70_13",
            "CMN_EDT_PSP01_70_14",
            "CMN_EDT_PSP01_70_15",
            "CMN_EDT_PSP01_70_16",
            "CMN_EDT_PSP01_70_17",
            "CMN_EDT_PSP01_70_18",
            "CMN_EDT_PSP01_70_20",
            "CMN_EDT_PSP01_70_21",
            "CMN_EDT_PSP01_70_23",
            "CMN_EDT_PSP01_70_24",
            "CMN_EDT_PSP01_70_29",
            "CMN_EDT_PSP01_70_31",
            "CMN_EDT_PSP01_70_32",
            "CMN_EDT_PSP02_002A",
            "CMN_EDT_PSP02_005A",
            "CMN_EDT_PSP02_011A",
            "CMN_EDT_PSP02_013A",
            "CMN_EDT_PSP02_014A",
            "CMN_EDT_PSP02_016A",
            "CMN_EDT_PSP02_017A",
            "CMN_EDT_PSP02_024A",
            "CMN_EDT_PSP02_034A",
            "CMN_EDT_PSP02_038A",
            "CMN_EDT_PSP02_039A",
            "CMN_EDT_PSP02_053A",
            "CMN_EDT_PSP02_056A",
            "CMN_EDT_PV441_001",
            "CMN_EDT_VITA01_002A",
            "CMN_EDT_VITA01_003A",
            "CMN_EDT_VITA01_009A",
            "CMN_EDT_VITA01_012A",
            "CMN_EDT_VITA01_014A",
            "CMN_EDT_VITA01_015A",
            "CMN_EDT_VITA01_016A",
            "CMN_EDT_VITA01_018A",
            "CMN_EDT_VITA01_IDLE",
        };
    }
}